package com.pickgliss.loader
{
	import com.pickgliss.ui.ComponentSetting;
	
	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.net.SharedObject;
	import flash.net.SharedObjectFlushStatus;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.Timer;
	import flash.utils.getTimer;

	/**
	 * 缓存文件原理：
	 * 1.缓存文件目录。file[path] = md5;
	 * 2.缓存文件  shareobject[path] = object;
	 * 首先判断缓存目录中是否有该路径的文件，如果有，则通过路径取得缓存文件。
	 * @author alan
	 * 
	 */	
	public class LoaderSavingManager extends EventDispatcher
	{
		public function LoaderSavingManager()
		{
		}
		public static const SO_OPEN    : String = "SharedObject.Flush.Open";
		public static const SO_FAILED  : String = "SharedObject.Flush.Failed";
		public static const SO_SUCCESS : String = "SharedObject.Flush.Success";
		private static var _eventDispatcher:LoaderSavingManager;
		public static function get eventDispatcher():LoaderSavingManager
		{
			if(!_eventDispatcher)_eventDispatcher=new LoaderSavingManager();
			return _eventDispatcher;
		}
		
		private static const LOCAL_FILE:String = "7road/files";
		private static var _cacheFile:Boolean = false;
		private static var _files:Object;  //缓存数据
		private static var _saveTimer:Timer;
		private static var _so:SharedObject;  //本地缓存文件
		private static var _changed:Boolean;  //是否有数据变动需要缓存
		private static var _save:Array;  //保存了待写缓存的对象
		private static var _reg : RegExp;
		private static var _version:int;
		
		public static function set cacheAble(value:Boolean):void
		{
			_cacheFile = value;
			if(!value && _save)
			{
				while(_save.length > 1)_save.pop();
				_changed = false;
			}
		}
		
		public static function get cacheAble():Boolean
		{
			return _cacheFile;
		}
		private static var isSetup : Boolean;
		public static function setup():void
		{
			if(isSetup)return;
			_reg = new RegExp(ComponentSetting.RESOURCE_PATH);
			isSetup = true;
			cacheAble = false;
			_save = new Array();
			loadFilesInLocal(false);
		}
		
		/**
		 * 解析config.xml的version节点，并根据配置的update信息更新缓存，支持*的通配符,返回是否进行过更新的结果
		 * @param config
		 *  <version from="versionid" to="vid">
		 * 		<file value="*" />
		 * 		<file value="image/*.png" />
		 *  </version>
		 */		
		public static function parseUpdate(config:XML):Boolean
		{
			try
			{
				var vs:XMLList = config..version;
				for each(var unode:XML in vs)
				{
					var fromv:int = int(unode.@from);
					var tov:int = int(unode.@to);
					var fs:XMLList = unode..file;
					var updatelist:Array = new Array();
					for each(var fn:XML in fs)
					{
						updatelist.push(String(fn.@value))
					}
					applyUpdate(fromv,tov,updatelist);
				}
			}
			catch(e:Error)
			{//更新失败，清除所有的缓存
				_version = -1;
				if(_so)_so.data["data"] = _files = new Object();
				_changed = true;
			}
			saveFilesToLocal();
			return _changed;
		}
		
		/**
		 * 根据配置的更新信息更新缓存
		 */		
		public static function applyUpdate(fromv:int,tov:int,updatelist:Array):void
		{
			if(tov <= fromv) return;
			if(_version < tov)
			{
				if(_version < fromv)
				{//当前版本太旧，清除所有缓存
					_so.data["data"] = _files = new Object();
				}
				else
				{
					var updated:Array = new Array();
					for each(var s:String in updatelist)
					{
						var t:String = getPath(s);
						t = t.replace("*","\\w*");
						updated.push(new RegExp("^"+t));
						updated.push(new RegExp(t));
					}
					
					var temp:Array = new Array();
					for(var f:String in _files)
					{
						if(hasUpdate(f,updated))
						{
							temp.push(f);
						}
					}
					
					for each(var n:String in temp)
					{
						trace("clear cache:",n);
						delete _files[n];
					}
				}
				_version = tov;
				_files["version"] = tov;
				_changed = true;
			}
		}
		
		/**
		 * 资源是否有更新
		 */		
		private static function hasUpdate(path:String,updateList:Array):Boolean
		{
			for each(var s:RegExp in updateList)
			{
				if(path.match(s))
				{
					return true;
				}
			}
			return false;
		}
			
		/**
		 * 将本地缓存中原来版本控制的文件更新为md5控制
		 */		
		public static function updateVersionToMD5(updatelist:Dictionary):void
		{
			var updated:Array = new Array();
			for(var filepath:String in updatelist)
			{
				var path:String = getPath(filepath);
				var isEquial:Boolean = (_files[path] == updatelist[filepath]);
				var oldExits : Boolean =  (_files[path] == true || _files[path] == "7road")
				/**
				 * 1 地址是否为空
				 * 2 key是否更新
				 * 3 文件是否存在老文件，有true,7road
				 * 7road是当前md5列表中不存在该文件，在以后的版本中加入该文件
				*/
				if(path && !isEquial && oldExits)
				{
					_changed = true;
					_files[path] = updatelist[filepath];
				}
			}
		}
		
		/**
		 * 根据path清除缓存
		 */		
		public static function clearFiles(filter:String):void
		{
			if(_files)
			{
				var t:String = getPath(filter);
				delete _files[t];
				try
				{
					if(cacheAble)
					{
						openSO();
					}
				}
				catch(e:Error){}
			}
		}
		
		/**
		 * 获取本地缓存文件目录
		 */		
		public static function loadFilesInLocal(isOpen:Boolean=false):void
		{
			try
			{
				if(!_so)_so = SharedObject.getLocal(LOCAL_FILE,"/");
				_so.addEventListener(NetStatusEvent.NET_STATUS,__netStatus);
				_files = _so.data["data"];
				if(_files == null)
				{
					_files = new Object();
					_so.data["data"] = _files;
					cacheAble = false;
					if(isOpen)openSO();					
				}
				else
				{
					_version = _files["version"];
					cacheAble = true;
					if(isOpen)openSO();
				}
			}
			catch(e:Error)
			{
			}
		}
		/**
		 * 检测flash缓存是否开启
		 */		
		public static function checkCloseShardObject() : int
		{
			_save;
			try
			{
				if(!_so)_so = SharedObject.getLocal(LOCAL_FILE,"/");
				var state : String = _so.flush(20 * 1024 * 1024);
				if(state == SharedObjectFlushStatus.PENDING)
				{
					eventDispatcher.dispatchEvent(new Event(SO_OPEN));
					return 1;
				}
				else
				{
					cacheAble = true;
					return 2;
				}
			}
			catch(e:Error)
			{
				eventDispatcher.dispatchEvent(new Event(SO_FAILED));
				return 0;
			}
			return 0;
		}
		
		/**
		 * 检测flash缓存是否开启
		 */		
		private static function openSO() : void
		{
			var state : String = _so.flush(20 * 1024 * 1024);
			if(state == SharedObjectFlushStatus.PENDING)
			{
				eventDispatcher.dispatchEvent(new Event(SO_OPEN));
			}
			else
			{
				cacheAble = true;
			}
		}
		
		/**
		 * 替换掉http://..../ 
		 */		
		private static const _reg1:RegExp = /http:\/\/[\w|.|:]+\//i;
		/**
		 * 替换: . / 
		 */		
		private static const _reg2:RegExp = /[:|.|\/]/g;
		private static function getPath(path:String):String
		{//只取问号前面部分
			var index:int = path.indexOf("?");
			if(index != -1)
			{
				path = path.substring(0,index)
			}
			path = path.replace(_reg1,"");
			return path.replace(_reg2,"-");
		}
		
		/**
		 *右键清理缓存 
		 */
		public static function cleanTempSaveFile( ) :void
		{
			_so.data["data"] = _files = new Object();
			try
			{
				_so.flush();
			} 
			catch(error:Error) {}
			_changed = true;
		}
		
		private static var _isSaving:Boolean = false;
		private static var _shape:Shape = new Shape();
		/**
		 * 将缓存写到本地
		 */		
		public static function saveFilesToLocal():void
		{
			try
			{
				if(_files && _changed && cacheAble && !_isSaving)
				{
					_isSaving = true;
					_shape.addEventListener(Event.ENTER_FRAME,save);
				}
			}
			catch(e:Error){}
		}
		
		private static function save(event:Event):void
		{
			try
			{
				var state:String = _so.flush(20 * 1024 * 1024);
				if(state != SharedObjectFlushStatus.PENDING)
				{
					var tick:int = getTimer();
					if(_save.length > 0)
					{
						var obj:Object = _save.shift();
						if(!obj || obj.d == null)return;
						var so:SharedObject = SharedObject.getLocal(obj.p,"/");
						so.data["data"] = obj.d;
						so.flush();
						so = null;
						_files[obj.p] = obj.md5;
						_so.flush();
						trace("save local data " + obj.p + "  md5" + obj.md5 + "spend:",getTimer() - tick,"  left:",_save.length);
					}
					if(_save.length == 0)
					{
						_shape.removeEventListener(Event.ENTER_FRAME,save);
						_changed = false;
						_isSaving = false;
					}
				}
			}catch(e:Error)
			{
				_shape.removeEventListener(Event.ENTER_FRAME,save);
				_isSaving = false;
			}
		}
		
		public static function loadCachedFile(path:String,cacheInMem:Boolean,md5:String=""):ByteArray
		{
			if(_files)
			{
				var p:String = getPath(path);
				var tick:int = getTimer();
				var f:ByteArray = findInSave(p);
				if(f == null && _files[p])
				{
					if(_files[p] == md5)
					{
						var so:SharedObject = SharedObject.getLocal(p,"/");
						f = ByteArray(so.data["data"]);
					}
					else
					{
						delete _files[p];
					}
				
				}
				if(f)
				{
					trace("get{local:",getTimer() - tick,"ms}",path);
					return f;
				}
				delete _files[p];
			}
			trace("get{network}::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + (count ++),path);
			return null;
		}
		private static var count : int;
		
		/**
		 * 根据path获取缓存文件
		 */		
		private static function findInSave(path:String):ByteArray
		{
			for each(var cache:Object in _save)
			{
				if(cache.p == path)return ByteArray(cache.d);
			}
			return null;
		}
		
		/**
		 * 添加到待写缓存列表
		 */		
		public static function cacheFile(path:String,data:ByteArray,cacheInMem:Boolean,md5:String=""):void
		{
			if(!cacheAble || data == null || data.length == 0)return;
			var p:String = getPath(path);
			if(_files[p])return;
			if(_files)
			{			
				_save.push({p:p,md5:md5,d:data});
				if(!md5)
				{
					trace();
				}
				_changed = true;
			}
		}
		
		private static var _retryCount:int = 0;
		private static function __netStatus(event:NetStatusEvent):void
		{
			switch (event.info.code) 
			{//第一次用户同意时，会保存失败，重试2次还失败，放弃
				case "SharedObject.Flush.Failed":
					if(_retryCount < 1)
					{
						try
						{
							_so.flush(20 * 1024 * 1024);
						}
						catch(e:Error)
						{
						}
						_retryCount ++;
					}
					else
					{
						cacheAble = false;
						eventDispatcher.dispatchEvent(new Event(SO_FAILED));
					}
					break;
				case "SharedObject.Flush.Success":
					cacheAble = true;
					eventDispatcher.dispatchEvent(new Event(SO_SUCCESS));
				default:
					_retryCount = 0;
					break;
			}
		}
		
		/**
		 * 是否有数据需要缓存
		 */		
		public static function  get hasFileToSave():Boolean
		{
			return cacheAble && _changed;
		}
		/**
		 * 检查资源是否已缓存
		 * @param path　资源url
		 */		
		public static function checkIsCached(path : String):Boolean
		{
			if(_files && _files[getPath(path)])
			{
				return _files[getPath(path)];
			}
			for each(var obj:Object in _save)
			{
				if(obj['p'] == getPath(path))
					return obj['p'];
			}
			return false;
		}
	}
}